package smallfrench.moteur.biblio;

import smallfrench.moteur.biblio.err.ErreurParametres;
import smallfrench.moteur.interp.RObjet;

public class RClasseChaine extends RClasse {

	public static final RClasseChaine CLASSE = new RClasseChaine();

	private RClasseChaine() {
		super(new RClasse[] {RClasse.CLASSE, AIterable.CLASSE});
		champs.put("caractere", fnCaractere);
		champs.put("contient", fnContient);
		champs.put("enChaine", fnEnChaine);
		champs.put("estChiffres", fnEstChiffres);
		champs.put("estChiffresHexa", fnEstChiffresHexa);
		champs.put("estLettres", fnEstLettres);
		champs.put("estMajuscules", fnEstMajuscules);
		champs.put("estMinuscules", fnEstMinuscules);
		champs.put("iterateur", fnIterateur);
		champs.put("majuscule", fnMajuscule);
		champs.put("minuscule", fnMinuscule);
		champs.put("partie", fnPartie);
		champs.put("taille", fnTaille);
	}

	@Override
	public RObjet appeler(RObjet moi, RObjet[] params) {
		if (params.length == 0) {
			return new RChaine("");
		} else if (params.length == 1) {
			return params[0].enChaine();
		} else {
			throw ErreurParametres.nouvelleException();
		}
	}

	@Override
	public RChaine enChaine() {
		return new RChaine("Chaine");
	}

	//------------------------------------------- Statics

	private static abstract class Fonction extends RFonction {

		public abstract RObjet appeler2(RChaine moi, RObjet[] params);

		@Override
		public final RObjet appeler(RObjet moi, RObjet[] params) {
			if (moi instanceof RChaine)
				return appeler2((RChaine)moi, params);
			return null;
		}
	}

	//-------------------------------------------

	/**
	 * Accès au caractère d’un index i.
	 */
	private Fonction fnCaractere = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 1)
				throw ErreurParametres.nouvelleException();

			RObjet r = moi.operateurIndexBase(params[0]);
			if (r == null)
				new ErreurParametres().lancer();
			return r;
		}
	};

	/**
	 * Retourne vrai si la chaine contient la chaine passée en paramètre.
	 */
	private Fonction fnContient = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 1)
				throw ErreurParametres.nouvelleException();

			if (! (params[0] instanceof RChaine))
				throw ErreurParametres.nouvelleException();

			RChaine param = (RChaine)params[0];

			return RBooleen.nouveau(moi.valeur.contains(param.valeur));
		}
	};

	private Fonction fnEstChiffres = new Fonction () {
		@Override
		public RBooleen appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 0)
				throw ErreurParametres.nouvelleException();

			String valeur = moi.valeur;

			if (valeur.length() == 0)
				return RBooleen.FAUX;

			for (int i = 0; i < valeur.length(); i++) {
				if (Character.digit(valeur.charAt(i), 10) == -1) {
					return RBooleen.FAUX;
				}
			}
			return RBooleen.VRAI;
		}
	};

	private Fonction fnEstChiffresHexa = new Fonction () {
		@Override
		public RBooleen appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 0)
				throw ErreurParametres.nouvelleException();

			String valeur = moi.valeur;

			if (valeur.length() == 0)
				return RBooleen.FAUX;

			for (int i = 0; i < valeur.length(); i++) {
				if (Character.digit(valeur.charAt(i), 16) == -1) {
					return RBooleen.FAUX;
				}
			}
			return RBooleen.VRAI;
		}
	};

	private Fonction fnEstLettres = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 0)
				throw ErreurParametres.nouvelleException();

			String valeur = moi.valeur;

			if (valeur.length() == 0)
				return RBooleen.FAUX;

			for (int i = 0; i < valeur.length(); i++) {
				if (! Character.isLetter(valeur.charAt(i))) {
					return RBooleen.FAUX;
				}
			}
			return RBooleen.VRAI;
		}
	};

	private Fonction fnEstMajuscules = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 0)
				throw ErreurParametres.nouvelleException();

			String valeur = moi.valeur;

			if (valeur.length() == 0)
				return RBooleen.FAUX;

			for (int i = 0; i < valeur.length(); i++) {
				if (! Character.isUpperCase(valeur.charAt(i))) {
					return RBooleen.FAUX;
				}
			}
			return RBooleen.VRAI;
		}
	};

	private Fonction fnEstMinuscules = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 0)
				throw ErreurParametres.nouvelleException();

			String valeur = moi.valeur;

			if (valeur.length() == 0)
				return RBooleen.FAUX;

			for (int i = 0; i < valeur.length(); i++) {
				if (! Character.isLowerCase(valeur.charAt(i))) {
					return RBooleen.FAUX;
				}
			}
			return RBooleen.VRAI;
		}
	};

	/**
	 * Retourne vrai si la chaine contient la chaine passée en paramètre.
	 */
	private Fonction fnEnChaine = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 0)
				throw ErreurParametres.nouvelleException();

			return moi;
		}
	};

	/**
	 * Retourne vrai si la chaine contient la chaine passée en paramètre.
	 */
	private Fonction fnIterateur = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {
			if (params.length != 0)
				throw ErreurParametres.nouvelleException();
			return new AIterateurChaine(moi.valeur);
		}
	};

	/**
	 * Retourne la longueur de la chaine
	 */
	private Fonction fnTaille = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {
			return new REntier(moi.valeur.length());
		}
	};

	/**
	 * Convertit la Chaine en minuscules
	 */
	private Fonction fnMinuscule = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {
			return new RChaine(moi.valeur.toLowerCase());
		}
	};

	/**
	 * Convertit la Chaine en majuscules
	 */
	private Fonction fnMajuscule = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {
			return new RChaine(moi.valeur.toUpperCase());
		}
	};

	/**
	 * Retourne une partie d’une chaine.
	 * Equivalent String.substring(int, int)
	 */
	private Fonction fnPartie = new Fonction () {
		@Override
		public RObjet appeler2(RChaine moi, RObjet[] params) {

			if (params.length != 2)
				throw ErreurParametres.nouvelleException();

			RObjet arg0 = params[0],
					arg1 = params[1];

			if (! (arg0 instanceof REntier) || ! (arg1 instanceof REntier))
				throw ErreurParametres.nouvelleException();

			REntier e0 = (REntier)arg0;
			REntier e1 = (REntier)arg1;
			int debut = e0.getValeurInt();
			int fin = e1.getValeurInt();

			String maValeur = moi.valeur;

			if (debut < 0 || fin > maValeur.length()
					|| debut > fin)
				return null;

			return new RChaine(maValeur.substring(debut, fin));
		}
	};

	//
	//	/** Retourne le point de code Unicode du premier caractère de la
	//	 * chaine. Si la chaine est vide, retourne <code>rien</code>.
	//	 */
	//	pointDeCode(new Fonction () {
	//		@Override
	//		public RObjet appeler2(RChaine moi, RObjet[] params)
	//				throws RThrowable {
	//
	//			String s = moi.valeur;
	//
	//			if (s.length() == 0)
	//				return null;
	//
	//			return new REntier(s.charAt(0));
	//		}
	//	}),
	//
	//	pointsDeCode(new Fonction () {
	//		@Override
	//		public RObjet appeler2(RChaine moi, RObjet[] params)
	//				throws RThrowable {
	//
	//			String s = moi.valeur;
	//
	//			ArrayList<RObjet> tableau = new ArrayList<RObjet>();
	//			for (int i = 0 ; i < s.length() ; i++) {
	//				tableau.add(new REntier(s.charAt(i)));
	//			}
	//
	//			return new ATableau(tableau);
	//		}
	//	}),


	public static class AIterateurChaine extends AIterateur {

		public static final AClasse CLASSE = AClasse.creer(
				AIterateurChaine.class, "IterateurChaine");

		private final String string;
		private int index = 0;

		@AnnotationMembre
		public AIterateurChaine(String string) {
			this.string = string;
		}

		@Override
		@AnnotationMembre
		public RObjet suivant() {
			if (index < string.length())
				return new RChaine(string.charAt(index++));
			else
				return RRien.RIEN;
		}

		@Override
		@AnnotationMembre
		public RBooleen contientAutres() {
			return RBooleen.nouveau(index < string.length());
		}
	}
}
